package tech

import "ssp/set"

func (s *Solve) ExtendedUniqueRectangle(g *Grid) (used bool, results []*Result) {
	for startCol := 0; startCol < 9; startCol += 3 {
		if ok, rs := g.eURLines(g.rows[:], startCol); ok {
			used = true
			results = append(results, rs...)
			return
		}
	}

	for startCol := 0; startCol < 9; startCol += 3 {
		if ok, rs := g.eURLines(g.cols[:], startCol); ok {
			used = true
			results = append(results, rs...)
			return
		}
	}
	return
}

func (g *Grid) eURLines(rows []House, startCol int) (used bool, results []*Result) {
	for i := 0; i < 3; i++ {
		for j := 3; j < 6; j++ {
			for k := 6; k < 9; k++ {
				for _, cols := range [][2]int{{startCol + 0, startCol + 1}, {startCol + 1, startCol + 2}, {startCol + 0, startCol + 2}} {
					if ok, rs := g.doEUR(rows, [3]int{i, j, k}, cols); ok {
						used = true
						results = append(results, rs...)
					}
				}
			}
		}
	}
	return
}

func (g *Grid) doEUR(rows []House, rowNums [3]int, colNums [2]int) (used bool, results []*Result) {
	var ps [3][2]*Cell
	for i, rowNum := range rowNums {
		for j, colNum := range colNums {
			ps[i][j] = rows[rowNum].cells[colNum]
			if ps[i][j].solved() {
				return
			}
		}
	}
	if ok, result := g.doEURType1(ps); ok {
		used = true
		results = append(results, result)
		return
	}
	/*
		if ok, result := g.doEURType2(ps); ok {
			used = true
			results = append(results, result)
		}

		if ok, result := g.doEURType4(ps); ok {
			used = true
			results = append(results, result)
		}
	*/
	return
}

func (g *Grid) doEURType1(ps [3][2]*Cell) (used bool, result *Result) {
	candss := [2]set.Set{}
	for i := range 3 {
		for j := range 2 {
			candss[j] = candss[j].Union(ps[i][j].cands)
		}
	}

	var rmj int
	var cands, otherCands set.Set
	var rmCell *Cell

	if candss[0].Count() == 3 {
		rmj = 1
		cands = candss[0]
		otherCands = candss[1].Diff(cands)
	} else if candss[1].Count() == 3 {
		rmj = 0
		cands = candss[1]
		otherCands = candss[0].Diff(cands)
	} else {
		return
	}

	for i := range 3 {
		if ps[i][rmj].cands.IsSuperset(otherCands) {
			rmCell = ps[i][rmj]
			break
		}
	}
	cells := []*Cell{}
	for i := range 3 {
		for j := range 2 {
			if ps[i][j] == rmCell {
				continue
			}
			cells = append(cells, ps[i][j])
		}
	}

	allCands := unionCellCands(cells)
	if allCands.Count() != 3 {
		return
	}
	result = new(Result)
	result.addRemoved(cands, []*Cell{rmCell})
	g1 := result.getHelpCandsGroup()
	g1.add(cands, cells)
	result.setStrategyName("Extended Unique Rectangle Type1")
	used = true
	return
}

func (g *Grid) doEURType2(ps [3][2]*Cell) (used bool, result *Result) {
	cells := []*Cell{}
	for i := range 3 {
		for j := range 2 {
			cells = append(cells, ps[i][j])
		}
	}
	cands := unionCellCands(cells)
	if cands.Count() != 4 {
		return
	}

	candCounts := [9][2]int{}
	for i := range 3 {
		for j := range 2 {
			for _, cand := range ps[i][j].cands.GetList() {
				candCounts[cand][j]++
			}
		}
	}
	zCands := []int{}
	for _, cand := range cands.GetList() {
		if candCounts[cand][0] == 1 || candCounts[cand][1] == 1 {
			zCands = append(zCands, cand)
		}
	}
	if len(zCands) != 1 {
		return
	}
	zCand := zCands[0]
	zCells := []*Cell{}
	for i := range 3 {
		for j := range 2 {
			if ps[i][j].cands.Has(zCand) {
				zCells = append(zCells, ps[i][j])
			}
		}
	}
	if ok, rmCells := g.canRemoveSeenCands(set.NewSet(zCand), zCells); ok {
		result = new(Result)
		result.addRemoved(set.NewSet(zCand), rmCells)
		g1 := result.getHelpCandsGroup()
		g1.add(set.NewSet(zCand), zCells)
		g2 := result.getHelpCandsGroup()
		g2.add(cands.Diff(set.NewSet(zCand)), cells)
		result.setStrategyName("Extended Unique Rectangle Type2")
		used = true
	}

	return
}

func (g *Grid) doEURType4(ps [3][2]*Cell) (used bool, result *Result) {
	candss := [3]set.Set{}
	for i := range 3 {
		candss[i] = ps[i][0].cands.Union(ps[i][1].cands)
	}
	var zCells []*Cell
	var cands set.Set
	var cells []*Cell

	if cands = candss[0].Union(candss[1]); cands.Count() == 3 {
		zCells = ps[2][:]
		cells = append(ps[0][:], ps[1][:]...)
	} else if cands = candss[1].Union(candss[2]); cands.Count() == 3 {
		zCells = ps[0][:]
		cells = append(ps[1][:], ps[2][:]...)
	} else if cands = candss[0].Union(candss[2]); cands.Count() == 3 {
		zCells = ps[1][:]
		cells = append(ps[0][:], ps[2][:]...)
	} else {
		return
	}
	zCands := unionCellCands(zCells).Intersect(cands)
	if count := zCands.Count(); count != 2 && count != 3 {
		return
	}
	strongCand := -1
	var line House
	if zCells[0].rowNum == zCells[1].rowNum {
		line = g.rows[zCells[0].rowNum]
	} else {
		line = g.cols[zCells[0].colNum]
	}
	for _, cand := range zCands.GetList() {
		if onlyCellsHasCand(cand, line, zCells) {
			strongCand = cand
		}
	}
	if strongCand == -1 {
		return
	}
	rmCands := zCands.Diff(set.NewSet(strongCand))
	result = new(Result)
	result.addRemoved(rmCands, zCells)
	g1 := result.getHelpCandsGroup()
	g1.add(cands, cells)
	g1.add(set.NewSet(strongCand), zCells)
	result.setStrategyName("Extended Unique Rectangle Type4")
	used = true

	return
}

func (s *Solve) ExtendedUniqueRectangleB(g *Grid) (used bool, results []*Result) {
	for startRow := 0; startRow < 9; startRow += 3 {
		if ok, rs := g.eURLinesB(g.rows[:], startRow); ok {
			used = true
			results = append(results, rs...)
			return
		}
	}

	for startCol := 0; startCol < 9; startCol += 3 {
		if ok, rs := g.eURLinesB(g.cols[:], startCol); ok {
			used = true
			results = append(results, rs...)
			return
		}
	}
	return
}

func (g *Grid) eURLinesB(rows []House, startRow int) (used bool, results []*Result) {
	rowNums := [3]int{startRow + 0, startRow + 1, startRow + 2}
	for i := 0; i < 6; i++ {
		for j := i/3*3 + 3; j < 9; j++ {
			if ok, rs := g.doEUR(rows, rowNums, [2]int{i, j}); ok {
				used = true
				results = append(results, rs...)
			}
		}
	}
	return
}
